﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;

namespace PrototypeZGame
{
    public class Unit : Actor
    {
        public Controller Controller;
        public float AP, APRegenRate, maxAP, APWalk, APRun;
        public float handsReach;
        public float viewDistance;
        public static int unitsCount = 0;
        public bool running;
        public float HP, maxHP;
        public bool dead;
        public Inventory inventory;
        public Int64 time_wasAttacking;
        public Int64 time_wasAttacked;
        public int swapCount;
        public int winCount;


        protected float _baseAttackStrength;
        public float attackStrength
        {
            get
            {
                if (inventory != null)
                {
                    Weapon weapon = inventory.rightHand as Weapon;
                    if (weapon != null)
                    {
                        return _baseAttackStrength + weapon.damage;
                    }
                }

                return _baseAttackStrength;
            }
        }
        

        public Unit(int X, int Y, World world)
            : base(X, Y, world)
        {
            tileset = world.tileset;
            _blocking = Result.BlockingUnit;
            unitsCount++;
            name = "Unit_"+unitsCount;
            running = false;
            dead = false;
            _moveCost = 10;

            time_wasAttacked = -10;
            time_wasAttacking = -10;
            winCount = 0;
        }


        public void PossesedBy(Controller controller)
        {
            if (Controller != null)
            {
                Controller.UnPosses();
                Controller = null;
            }
            Controller = controller;
        }

        public void UnPossesed()
        {
            Controller = null;
        }

        public void swap(Unit _other)
        {
            Vec2 otherPosition = _other.position;
            _other.moveTo(position);
            moveTo(otherPosition);
            _other.swapped();
        }

        public void swapped()
        {
            swapCount = 2;
        }

        public virtual Result walk(Direction direction)
        {
            if (running)
            {
                if (AP < APRun)
                {
                    return Result.NoAP;
                }

            }
            else
            {
                if (AP < APWalk)
                {
                    return Result.NoAP;
                }
            }

            int dx = 0, dy = 0;

            switch (direction) 
            {
                case Direction.North:
                    dy = -1; break;                        
                case Direction.South:
                    dy = 1; break;
                case Direction.West:
                    dx = -1; break;
                case Direction.East:
                    dx = 1; break;
                default:
                    break;
            }

            Result result = terrain.blocking(position.x + dx, position.y + dy);
            if (result == Result.NoBlock)
            {
                move(dx, dy);
                if (running)
                {
                    AP -= APRun;
                }
                else
                {
                    AP -= APWalk;
                }
                return result;
            }
            else
            {
                return result;
            }

        }


        public virtual void newTurn()
        {
            AP = Math.Min(AP + APRegenRate, maxAP);
            if (swapCount > 0) swapCount--;
        }

        public virtual string getInfoString()
        {
            string ret = name + "\n"
                + HP + "/" + maxHP + "HP\n" +
                +AP + "/" + maxAP + "(+" + APRegenRate + ")\n"
                + handsReach + "\n";
            if (Controller != null)
            {
                ret += Controller.ToString();
            }
            
            return ret;
        }

        public bool Attacked(Unit attacker, float dmg)
        {
            time_wasAttacked = world.time;
            HP -= dmg;
            if (HP <= 0)
            {
                Die();
                return true;
            }
            return false;
        }

        public virtual void Die()
        {
            Controller.UnitDied();
            _blocking = Result.NoBlock;
            dead = true;
            if (inventory != null)
            {
                dropInventory();
            }
        }

        public virtual bool Attack(Unit defender)
        {
            if (AP < 1)
            {
                return false;
            }

            float dmg = (float)world.rand.NextDouble()*attackStrength;
            if (defender.Attacked(this, dmg))
            {
                winCount++;
            }
            AP -= 1;
            time_wasAttacking = world.time;
            return true;
        }

        public virtual bool Attack(Door defender)
        {
            if (AP < 1)
            {
                return false;
            }

            float dmg = (float) world.rand.NextDouble() * attackStrength;
            defender.Attacked(this, dmg);
            AP -= 1;
            return true;
        }

        public Result takeItem(int num)
        {
            if (AP < 1) return Result.NoAP;
            if (inventory.freeSpace <= 0) return Result.InventoryFull;
            if (num >= terrain.getField(position).onFieldInventoryItems.Count) return Result.OutOfRange;
            
            InventoryItem item = terrain.getField(position).onFieldInventoryItems[num];
            
            inventory.putIn(item);
            terrain.takeFrom(item);
            item.position = new Vec2(-1);
            AP--;
            return Result.Success;
        }

        public Result takeItem(InventoryItem item)
        {
            if (AP < 1) return Result.NoAP;
            if (inventory.freeSpace <= 0) return Result.InventoryFull;
            if (!terrain.getField(position).onFieldInventoryItems.Contains(item)) return Result.CantFindItem;

            inventory.putIn(item);
            terrain.takeFrom(item);
            item.position = new Vec2(-1);
            AP--;
            return Result.Success;
        }

        public Result dropItem(int num)
        {
            if (AP < 1) return Result.NoAP;
            if (num >= inventory.Count) return Result.OutOfRange;

            InventoryItem item = inventory.takeFrom(num);
            item.position = position;
            terrain.getField(position).putOn(item);
            AP--;
            return Result.Success;
        }

        public void dropInventory()
        {
            while (inventory.Count > 0)
            {
                InventoryItem item = inventory.takeFrom(0);
                item.position = position;
                terrain.getField(position).putOn(item);
            }
        }

        public Result open(Door door)
        {
            if (AP >= 1)
            {
                AP--;
                door.open();
                return Result.Success;
            }
            else
            {
                return Result.NoAP;
            }

        }

        public Result close(Door door)
        {
            if (AP < 1) return Result.NoAP;

            AP--;
            door.close();
            return Result.Success;
        }

        public Result toggle(Door door)
        {
            if (AP < 1) return Result.NoAP;

            AP--;
            door.toggle();
            return Result.Success;
        }


        public virtual Result useItem(int num)
        {
            Debug.Assert(false);
            return Result.Success;
        }

        public override void Draw()
        {
            base.Draw();
            if (time_wasAttacked >= world.time-1)
            {
                Rectangle sourceRectangle = new Rectangle(((int)TileType.Status_Hit % 16) * fieldResolution, fieldResolution * ((int)TileType.Status_Hit / 16), fieldResolution, fieldResolution);
                world.spriteBatch.Draw(tileset, destinationRectangle, sourceRectangle, Color.White);
            }
            if (time_wasAttacking >= world.time-1)
            {
                Rectangle sourceRectangle = new Rectangle(((int)TileType.Status_WasAttacking % 16) * fieldResolution, fieldResolution * ((int)TileType.Status_WasAttacking / 16), fieldResolution, fieldResolution);
                world.spriteBatch.Draw(tileset, destinationRectangle, sourceRectangle, Color.White);
            }
        }




    }
}
